home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / Net / Geo.php < prev    next >
PHP Script  |  2004-03-24  |  20KB  |  646 lines

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP version 4.0                                                      |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group             |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.0 of the PHP license,       |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available at through the world-wide-web at                           |
  11. // | http://www.php.net/license/2_02.txt.                                 |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Graeme Merrall <graeme@inetix.com.au>                       |
  17. // |                                                                      |
  18. // +----------------------------------------------------------------------+
  19. //
  20. // $Id: Geo.php,v 1.5 2002/04/25 23:49:47 graeme Exp $
  21.  
  22. require_once 'PEAR.php';
  23. require_once 'Cache/Function.php';
  24.  
  25. /**
  26.  * NetGeo - determine geographic information on an internet address
  27.  *
  28.  * Can accept input of an AS number, an IP address or a host name
  29.  * Input can be individual or an array of addresses
  30.  *
  31.  * $geo = new NetGeo();
  32.  * $geo->getRecord("php.net");
  33.  * $geo->getRecord(array("php.net", "google.com"));
  34.  *
  35.  * Results returned are a single array of results if a string is passed in
  36.  * or in the case of an array, a multi-dim array with the as the key
  37.  *
  38.  * Query service type (CAIDA or localizer) is not available as a constructer
  39.  * to retain compatibility with existing CAIDA NetGeo classes (perl + java)
  40.  *
  41.  * @version 1.0
  42.  * @package NetGeo
  43.  * @author Graeme Merrall <graeme@inetix.com.au>
  44.  */
  45.  
  46. define('NETGEO_INPUT_ERROR', 'INPUT_ERROR');
  47. define('NETGEO_HTTP_ERROR', 'HTTP_ERROR');
  48. define('NETGEO_NO_MATCH', 'NO MATCH');
  49. define('NETGEO_NO_COUNTRY', 'NO_COUNTRY');
  50. define('NETGEO_LIMIT_EXCEEDED', 'NETGEO_LIMIT_EXCEEDED');
  51.  
  52. class Net_Geo
  53. {
  54.  
  55.     /**
  56.      * Path to local cache file. 
  57.      * Caching is compulsory to reduce load on CAIDA server
  58.      *
  59.      * @var string
  60.      * @access public
  61.      */
  62.     var $cache_path = "/tmp/";
  63.  
  64.     /**
  65.      * How long to wait befire rechecking cached entries in *days*
  66.      * This should be comething nice and high
  67.      *
  68.      * @var in
  69.      * @access public
  70.      */
  71.     var $cache_ttl = 30;
  72.  
  73.      /**
  74.      * CAIDA only
  75.      * 
  76.      * Maximum length of time, in seconds, which will be allowed during a whois
  77.      * lookup by the NetGeo server.
  78.      * The actual default value is maintained by the server.
  79.      *
  80.      * @var int
  81.      * @access public
  82.      */
  83.     var $default_timeout = 60;
  84.     
  85.     /**
  86.      * CAIDA only
  87.      * 
  88.      * Location of the default netgeo server
  89.      * If port not speicifed, defaults to 80
  90.      *
  91.      * @var string
  92.      * @access public
  93.      */
  94.     var $default_server = "http://netgeo.caida.org/perl/netgeo.cgi";
  95.  
  96.     /**
  97.      * localizer only
  98.      * 
  99.      * Location of the localizer data file
  100.      *
  101.      * @var string
  102.      * @access public
  103.      */
  104.     var $localizer_data = "./demo.csv";
  105.  
  106.     /**
  107.      * Type of service to use. May be either 'caida' or 'localizer'
  108.      * Default is 'caida'
  109.      *
  110.      * @var string
  111.      @ @access private
  112.      */
  113.     var $service;
  114.     
  115.     /**
  116.      * Cache filename prefix
  117.      *
  118.      * @var string
  119.      * @access private
  120.      */
  121.      var $cache_prefix = "netgeo";
  122.  
  123.     /**
  124.      * CAIDA only
  125.      * 
  126.      * User Agent string.
  127.      *
  128.      * @var string
  129.      * @access private
  130.      */
  131.     var $useragent = "PHP/NetGeo";
  132.  
  133.     /**
  134.      * CAIDA only
  135.      * 
  136.      * Class version
  137.      *
  138.      * @var string
  139.      * @access private
  140.      */
  141.     var $useragent_version = "1.0";
  142.  
  143.     /**
  144.      * CAIDA only
  145.      * 
  146.      * How many targets can be read in at once
  147.      * Should be enough for most everyone
  148.      *
  149.      * @var string
  150.      * @access private
  151.      */
  152.     var $array_limit = 100;
  153.  
  154.     /**
  155.      * Function cache object
  156.      *
  157.      * @var object
  158.      * @access private
  159.      */
  160.      var $cache;
  161.  
  162.     /**
  163.      * Name of global var for copying $this when calling function cache
  164.      * This is needed for the cache function to operate correctly
  165.      *
  166.      * @var string
  167.      * @access private
  168.      */
  169.      var $netgeo_global = "netgeo_global";
  170.  
  171.  
  172.     /**
  173.      * Constructor
  174.      * Both $applicationName and $alternateServerUrl are for compatibility
  175.      * with the perl and java netgeo classes.
  176.      * I don't guarantee to use these variables
  177.      *
  178.      * @param string $applicationName    Application using the NetGeo class.
  179.      * @param string $alternateServerUrl Alternate NetGeo server url
  180.      * @return bool
  181.      * @access public
  182.      */
  183.     function Net_Geo($applicationName="", $alternateServerUrl="")
  184.     {
  185.         $this->applicationName = $applicationName;
  186.         $this->alternateServerUrl = $alternateServerUrl;
  187.       
  188.         // init cache object
  189.         $this->cache = new Cache_Function('file', 
  190.                                           array('cache_dir' => $this->cache_path, 
  191.                                                 'filename_prefix' => $this->cache_prefix
  192.                                                ),
  193.                                           $this->cache_ttl * 86400
  194.                                          );
  195.  
  196.         return true;
  197.     }
  198.  
  199.     
  200.     function setService($service = "caida") {
  201.         
  202.         if ($service == "localizer") {
  203.             
  204.             if (@localizer_read($this->localizer_data, FALSE) == FALSE) {
  205.                 PEAR::raiseError("Can't read localizer data file ".$this->localizer_data);
  206.                 return false;
  207.             }
  208.  
  209.         } elseif ($service == "caida") {
  210.             
  211.             // check to see if an alternate server URL is used
  212.             if (!empty($alternateServerUrl)) {
  213.                 $this->default_server = $this->alternateServerUrl;
  214.             }
  215.     
  216.             $this->useragent = sprintf("%s %s", $this->useragent, 
  217.                                                 $this->useragent_version
  218.                                       );
  219.     
  220.             // set the custom user agent
  221.             if (!empty($applicationName)) {
  222.                 // trim whitespace
  223.                 $applicationName = trim($applicationName);
  224.     
  225.                 // also set the agent name
  226.                 $this->useragent = sprintf("%s/%s", $this->applicationName, 
  227.                                                     $this->useragent
  228.                                           );
  229.             }
  230.         
  231.         } else {
  232.             // return error
  233.             return new PEAR_Error("No service specified");
  234.  
  235.         }
  236.  
  237.  
  238.         $this->service = $service;
  239.         return true;
  240.             
  241.     }
  242.         
  243.     /**
  244.      * Gets a complete record for an address
  245.      * Returns either a single or multidimentional arrray
  246.      * if input is a string or an array respectively
  247.      *
  248.      * @param mixed $target Single or list of addresses
  249.      * @return array
  250.      * @access public
  251.      */
  252.     function getRecord($target)
  253.     {
  254.         return $this->_execute("getRecord", $target);
  255.     }
  256.  
  257.     /**
  258.      * Returns the 2-letter ISO 3166 country code
  259.      * Returns NO_MATCH if the AS number has been looked up
  260.      * but nothing was found in the whois lookups.
  261.      * Returns NO_COUNTRY if the lookup returned a record 
  262.      * but no country could be found.
  263.      * Returns an empty string if nothing was found in the database
  264.      *
  265.      * @param string $target single address
  266.      * @return array
  267.      * @access public
  268.      */
  269.     function getCountry($target)
  270.     {
  271.         $result = $this->_execute("getCountry", $target);
  272.         if (is_array($result)) {
  273.             return $result["COUNTRY"];
  274.         }
  275.  
  276.         return $result;
  277.     }
  278.  
  279.     /**
  280.      * Returns an array with keys LAT, LONG, LAT_LONG_GRAN, and STATUS.
  281.      * Lat/Long will be (0,0) if the target has been looked up but there was no
  282.      * match in the whois lookups, or if no address could be parsed from the
  283.      * whois record, or if the lat/long for the address is unknown.
  284.      * Returns an empty string if nothing was found in the database
  285.      *
  286.      * @param string $target single address
  287.      * @return array
  288.      * @access public
  289.      */
  290.     function getLatLong($target)
  291.     {
  292.         return $this->_execute("getLatLong", $target);
  293.     }
  294.  
  295.     /**
  296.      * Included here to make the NetGeo class as similar as possible to
  297.      * the NetGeoClient.java interface.
  298.      * It's probably just as easy for the user to extract lat and long directly
  299.      * from the array. 
  300.      *
  301.      * @param string $target single address
  302.      * @return double
  303.      * @access public
  304.      */
  305.     function getLat($latLongRef)
  306.     {
  307.         if (is_array($latLongRef)) {
  308.             $lat = $latLongRef["LAT"];
  309.         } else {
  310.             $lat = 0;
  311.         }
  312.  
  313.         return sprintf("%.2f", $lat);
  314.     }
  315.  
  316.     /**
  317.      * Included here to make the NetGeo class as similar as possible to
  318.      * the NetGeoClient.java interface.
  319.      * It's probably just as easy for the user to extract lat and long directly
  320.      * from the array
  321.      *
  322.      * @param string $target single address
  323.      * @return double
  324.      * @access public
  325.      */
  326.     function getLong($latLongHashRef)
  327.     {
  328.         if (is_array($latLongHashRef)) {
  329.             $long = $latLongHashRef["LONG"];
  330.         } else {
  331.             $long = 0;
  332.         }
  333.  
  334.         return sprintf("%.2f", $long);
  335.     }
  336.  
  337.     /**
  338.      * Interface to the public functions
  339.      *
  340.      * @param string $methodName Lookup method
  341.      * @param mixed  $target     Address(es) to lookup
  342.      * @return array
  343.      * @access private
  344.      */
  345.     function _execute($methodName, $input)
  346.     {
  347.  
  348.         // if we haven't got a service set, then do it now
  349.         if (empty($this->service)) {
  350.             $this->setService();
  351.         }
  352.  
  353.         // Test the target strings in the input array.  Any targets not in
  354.         // an acceptable format will have their STATUS field set to INPUT_ERROR.
  355.         // This method will also store the standardized target into the array
  356.         // for use as a key in the cache table.
  357.         $inputArray = $this->_verifyInputFormatArray($methodName, $input);
  358.         if (PEAR::isError($inputArray)) {
  359.             return $inputArray;
  360.         }
  361.  
  362.         $resultArray = $this->_processArray($methodName, $inputArray);
  363.         
  364.         // if there is only one array, move the whole thing up one
  365.         if (count($resultArray) == 1) {
  366.             $resultArray = $resultArray[0];
  367.         }
  368.  
  369.         return $resultArray;        
  370.     }   
  371.  
  372.     
  373.     /**
  374.      * Verify the type of the target argument and verify types of array elements
  375.      * Also converts the input array into the start of the output array
  376.      *
  377.      * @param string $methodName Lookup method
  378.      * @param mixed  $inputArray Address(es) to lookup
  379.      * @return array or pear error object on failure
  380.      * @access private
  381.      */
  382.     function _verifyInputFormatArray($methodName, $inputArray)
  383.     {
  384.         // makes sure that the input is an array
  385.         // if length is > than ARRAY_LIMIT_LENTH then bomb ou
  386.         if (count($inputArray) > $this->array_limit) {
  387.             // raise an error
  388.             $error = new PEAR_Error("Too many entries. Limit is ".$this->array_limit);
  389.             return $error;
  390.         }
  391.  
  392.         // convert into a useable array
  393.         $inputArray = $this->_convertInputArray($inputArray);
  394.         return $inputArray;
  395.     }
  396.  
  397.     /**
  398.      * Utility function to check what the input array
  399.      * and to convert to a correct array format for processing
  400.      *
  401.      * @param mixed $inputArray Address array
  402.      * @return array
  403.      * @access private
  404.      */
  405.     function _convertInputArray($inputArray)
  406.     {
  407.         // first check the darn thing is actually an array
  408.         if (!is_array($inputArray)) {
  409.             $inputArray = array($inputArray);
  410.         }
  411.     
  412.         // now convert to the correct array form
  413.         foreach ($inputArray as $entry) {
  414.             $returnArray[]["TARGET"] = $entry;
  415.         }
  416.         
  417.         return $returnArray;
  418.  
  419.     }
  420.  
  421.  
  422.     /** 
  423.      * Main function that processes addresses
  424.      *
  425.      * It might be a good idea to move the caching up one level?
  426.      * 
  427.      * @param string $methodName Lookup method
  428.      * @param array  $inputArray Formatted address array
  429.      * @return array
  430.      * @access private
  431.      */
  432.     function _processArray($methodName, $inputArray)
  433.     {
  434.         $i = 0;
  435.         foreach ($inputArray as $entry) {
  436.             $entry = $this->_verifyInputFormat($entry);
  437.         
  438.             if (isset($entry["TARGET"]) && !isset($entry["INPUT_ERROR"])) {
  439.  
  440.                 // set up the cache work around
  441.                 $GLOBALS[$this->netgeo_global] =& $this;
  442.                 
  443.                 if ($this->service == "localizer") {
  444.  
  445.                     $response = $this->cache->call('localizer_search', $entry["TARGET"]);
  446.  
  447.                 } else {
  448.  
  449.                     // else do the HTTP request
  450.                     $url = sprintf("%s?method=%s&target=%s", $this->default_server,
  451.                                                              $methodName, $entry["TARGET"]
  452.                                   );
  453.  
  454.                     $response =& $this->cache->call($this->netgeo_global.'->_executeHttpRequest', $url);
  455.     
  456.                 }
  457.  
  458.                 if (!isset($response)) {
  459.                     $entry["STATUS"] = NETGEO_HTTP_ERROR;
  460.                 }
  461.  
  462.                 // parse it all into something useful
  463.                 // at this point we should look for NETGEO_LIMIT_EXCEEDED as well
  464.                 $dataArray[$i] = $this->_processResult($response);
  465.                 
  466.             } else {
  467.                 $dataArray[$i] = $entry;
  468.             }
  469.  
  470.             $i++;
  471.         }
  472.         
  473.         if (is_array($dataArray)) {
  474.             return $dataArray;
  475.         } else {
  476.             return array("STATUS"=>NETGEO_HTTP_ERROR);
  477.         }
  478.     }
  479.  
  480.     /**
  481.      * Test the input and make sure it is in an acceptable format.  The input
  482.      * can be an AS number (with or without a leading "AS"), an IP address in
  483.      * dotted decimal format, or a domain name.  Stores the standardized targe
  484.      * string into the hash if input target is valid format, otherwise stores
  485.      * undef into hash.
  486.      * 
  487.      * @param array  $inputArray Address(es) to lookup
  488.      * @return array
  489.      * @access private
  490.  
  491.      */
  492.     function _verifyInputFormat($inputArray)
  493.     {
  494.         $target = trim($inputArray["TARGET"]);
  495.         
  496.         // look for AS|as
  497.         if (preg_match('/^(?:AS|as)?\s?(\d{1,})$/', $target, $matches)) {
  498.             // check the AS number. Btwn 1 and 65536
  499.             if ($matches[1] >= 1 && $matches[1] < 65536) {
  500.                 $standardizedTarget = $matches[0];
  501.             } else {
  502.                 $inputArray["INPUT_ERROR"] = NETGEO_INPUT_ERROR;
  503.                 // raise some error tex
  504.                 // Bad format for input. AS number must be between 1 and 65536
  505.                 return $inputArray;
  506.             }
  507.  
  508.         // IP number
  509.         } elseif (preg_match('/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/', $target, $matches)) {
  510.             if ($matches[1] <= 255 && $matches[2] <= 255 && $matches[3] <= 255 && $matches[4] <= 255) {
  511.                 $standardizedTarget = $target;
  512.             } else {
  513.                 $inputArray["INPUT_ERROR"] = NETGEO_INPUT_ERROR;
  514.                 // raise some error tex
  515.                 // Bad format for input. each octet in IP address must be between 0 and 255
  516.                 return $inputArray;
  517.             }
  518.  
  519.         // TLD
  520.         } elseif (preg_match('/^(?:[\w\-]+\.)*[\w\-]+\.([A-Za-z]{2,3})$/', $target, $matches)) {
  521.             $tld = $matches[1];
  522.             
  523.             // TLD length is either 2 or 3.  If length is 2 we just accept it,
  524.             // otherwise we test the TLD against the list.
  525.             if (strlen($tld) == 2 || preg_match('/^(com|net|org|edu|gov|mil|int)/i', $tld)) {
  526.                 $standardizedTarget = $target;
  527.             } else {
  528.                 $inputArray["INPUT_ERROR"] = NETGEO_INPUT_ERROR;
  529.                 // raise some error tex
  530.                 // Bad TLD in domain name. 3-letter TLDs must be one of com,net,org,edu,gov,mil,in
  531.                 return $inputArray;
  532.             }
  533.         } else {
  534.             $inputArray["INPUT_ERROR"] = NETGEO_INPUT_ERROR;
  535.             // raise some error text
  536.             // unrecognized format for input
  537.             return $inputArray;
  538.         }
  539.         
  540.         return $inputArray;
  541.                 
  542.     }
  543.     
  544.     /**
  545.      * Executes a request to the netgeo server
  546.      *
  547.      * @param array $inputArray Address(es) to lookup
  548.      * @return string Response from netgeo server
  549.      * @access private
  550.      */
  551.     function _executeHttpRequest($url)
  552.     {
  553.         $response = "";
  554.  
  555.         if (function_exists('curl_init')) {
  556.             $ch = curl_init($url);
  557.             curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  558.             $response = curl_exec($ch);
  559.             curl_close($ch);
  560.         } else {
  561.             // split the server url
  562.             $urlinfo = parse_url($url);
  563.             if (!isset($urlinfo["port"])) {
  564.                 $urlinfo["port"] = 80;
  565.             }
  566.         
  567.             $sp = @fsockopen($urlinfo["host"], $urlinfo["port"], &$errno, &$errstr, $this->default_timeout);
  568.             if (!$sp) {
  569.                 return false;
  570.             }
  571.     
  572.             fputs($sp, "GET " . $urlinfo["path"] ."?". $urlinfo["query"] . " HTTP/1.0\r\n");
  573.             fputs($sp, "User-Agent: " . $this->useragent . "\r\n\r\n");
  574.             while (!feof($sp)) {
  575.                 $response .= fgets($sp,128);
  576.             }
  577.             fclose ($sp);
  578.         }
  579.  
  580.         return $response;
  581.     }
  582.    
  583.     /**
  584.      * Parses the results from the server into an array
  585.      * 
  586.      * @param string $response Response from netgeo server
  587.      * @return array 
  588.      * @access private
  589.      */
  590.     function _processResult($response)
  591.     {
  592.         // process the localizer result differently
  593.         // since we already have an array
  594.         if ($this->service == "localizer") {
  595.  
  596.             foreach ($response as $key=>$val) {
  597.  
  598.                 $retarray[strtoupper($key)] = $val;
  599.             }
  600.  
  601.         } elseif ($this->service == "caida") {
  602.        
  603.             $lineArray = preg_split("/\n/", $response);
  604.             $line = array_shift($lineArray);
  605.  
  606.             // first check for anything icky from the server
  607.             if (preg_match("/".NETGEO_HTTP_ERROR."/", $line) || preg_match('/^\s*$/', $response)) {
  608.  
  609.                 // empty empty empty
  610.                 if (preg_match('/^\s*$/', $text)) {
  611.                     $text = "Empty content string";
  612.                     return array("STATUS"=>$text);
  613.                 }
  614.  
  615.             } elseif (preg_match("/".NETGEO_LIMIT_EXCEEDED."/", $line)) {
  616.                 return array("STATUS"=>$text);
  617.             }
  618.  
  619.             // now loop through. This should being us out at TARGET
  620.             while (isset($line) && !preg_match("/^TARGET:/", $line)) {
  621.                 $line = array_shift($lineArray);
  622.             }
  623.  
  624.             // keep going
  625.             while (isset($line)) {
  626.                 if (preg_match("/^TARGET:\s+(.*\S)\s*<br>/", $line, $matches)) {
  627.                     $retarray["TARGET"] = $matches[1];
  628.                 } elseif (preg_match("/^STATUS:\s+([\w\s]+\S)\s*<br>/", $line, $matches)) {
  629.                     $retarray["STATUS"] = $matches[1];
  630.                 } elseif (preg_match("/^(\w+):\s+(.*\S)\s*<br>/", $line, $matches)) {
  631.                     $retarray[$matches[1]] = $matches[2];
  632.                 }
  633.                 $line = array_shift($lineArray);
  634.             }
  635.  
  636.         }
  637.  
  638.         return $retarray;   
  639.  
  640.     }
  641.  
  642. }
  643.  
  644.  
  645. ?>
  646.